#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "Logger.h"
#include "../config/Config.h"

// ------------------------------------------------------------------ //

namespace kc_sf
{
extern const std::string DEF_LOG_FILE = "logs.txt";
const std::string Logger::DEF_LOG_LEVEL_NAME = "info";
const std::string Logger::LogLevelNames[] = 
    { "NOTHING", "FATAL", "ERROR", "INFO", "DETAIL", "DEBUG", "ALL",  };
}

using namespace kc_sf;

// ------------------------------------------------------------------ //

Logger::Logger()
: l_stream( LogStream::getInstance() ),
  l_log_level( Fatal ),
  l_cache( "" ),
  l_initiated( true )
{
	l_accepted_level = Config::getInstance().cLogsLevel;
}

Logger::Logger( const std::string &accepted_log_level_name ) // throws LogStream::OpenFailure 
: l_stream( LogStream::getInstance() ),
  l_accepted_level( mapLevelNameToInt( accepted_log_level_name ) ),
  l_log_level( Fatal ), l_cache( "" ), l_initiated( true )
{
}

Logger::Logger( const std::string &log_filename,
	bool append ) // throws LogStream::OpenFailure
: l_stream( LogStream::getInstance() ),
  l_log_level( Fatal ),
  l_cache( "" ),
  l_initiated( true )
{
	l_accepted_level = Config::getInstance().cLogsLevel;
}

Logger::Logger( const std::string &log_filename,
		bool append,
        const std::string &accepted_log_level_name ) // throws LogStream::OpenFailure
: l_stream( LogStream::getInstance() ),
  l_accepted_level( mapLevelNameToInt( accepted_log_level_name ) ),
  l_log_level( Fatal ), l_cache( "" ), l_initiated( true )
{
}

Logger::Logger( LogStream &stream ) // throws LogStream::OpenFailure
: l_stream( stream ), 
  l_log_level( Fatal ),
  l_cache( "" ),
  l_initiated( true )
{
	l_accepted_level = Config::getInstance().cLogsLevel;
}                

Logger::Logger( LogStream &stream, 
                const std::string &accepted_log_level_name  ) // throws LogStream::OpenFailure 
: l_stream( stream ), 
  l_accepted_level( mapLevelNameToInt( accepted_log_level_name ) ),
  l_log_level( Fatal ), l_cache( "" ), l_initiated( true )
{
}                

// ------------------------------------------------------------------ //

LogStream & Logger::getStream()
{
    return l_stream;
}

const LogStream & Logger::getStream() const
{
    return l_stream;
}

// Otwieranie i zamykanie strumienia logów -------------------------- //

bool Logger::open( bool append )
{
    return l_stream.open( append );
}

bool Logger::isOpened() const
{
    return l_stream.isOpened();
}

void Logger::close()
{
    l_stream.close();
}

// Ustawianie priorytetow ------------------------------------------- //

void Logger::setMsgPriority( LogLevel log_priority )
{
    l_log_level = log_priority;
}

// Zapis do logu ---------------------------------------------------- //

void Logger::write( LogLevel log_level, const std::string &log )
{
    if( l_accepted_level >= log_level 
        && log_level != Nothing )
    {
        l_cache += log;
        if( l_initiated )
            l_initiated = false;
    }
}

void Logger::write( const std::string &log )
{
    if( l_accepted_level >= l_log_level 
        && l_log_level != Nothing )
    {
        l_cache += log;
#ifdef DEBUG
std::cout << "Logger: mam cache: " << l_cache << std::endl;
#endif // DEBUG
        if( l_initiated )
            l_initiated = false;
    }
}

void Logger::write( LogLevel log_level, int nr )
{
    if( l_accepted_level >= log_level 
        && log_level != Nothing )
    {
        l_cache += boost::lexical_cast <std::string>( nr );
        if( l_initiated )
            l_initiated = false;
    }
}

void Logger::write( int nr )
{
    if( l_accepted_level >= l_log_level 
        && l_log_level != Nothing )
    {
        l_cache += boost::lexical_cast <std::string>( nr );
        if( l_initiated )
            l_initiated = false;
    }
}

void Logger::writeNewLine()
{
    if( !l_cache.empty() )
    {
        l_cache += "\n                    ";
        l_cache += "               \t\t\t\t";
    }
}

Logger & Logger::operator<<( const std::string &log_msg )
{
    write( log_msg );
    return *this;
}

Logger & Logger::operator<<( int nr )
{
    write( nr );
    return *this;
}

Logger & Logger::operator<<( ManipPtr manip )
{
    manip( *this );
    return *this;
}

// Metody statyczne ------------------------------------------------- //

LogLevel
Logger::mapLevelNameToInt( const std::string &log_level_name )
{
    if( boost::iequals( log_level_name, DEF_LOG_LEVEL_NAME ) )
        return DEF_LOG_LEVEL;
    
    for( int i = 0; i < LevelsNr; ++i )
    {
        if( i == DEF_LOG_LEVEL )
            continue;
        if( boost::iequals( log_level_name, LogLevelNames[i] ) )
            return (LogLevel)i;
    }
    
    return Info;
}

////////////////////////////////////////////////////////////////////////
// Manipulatory
////////////////////////////////////////////////////////////////////////

namespace kc_sf
{
    
	Logger & flush( Logger & logger )
	{
		if( logger.l_cache.empty() )
		return logger;

		std::string log_msg;
		if( !logger.l_initiated )
		{
			log_msg = Logger::LogLevelNames[ logger.l_log_level ];
			log_msg += " ";
		}
		log_msg += logger.l_cache;
		logger.l_stream.write( log_msg );
		logger.l_cache.clear();

		return logger;
	}

	Logger & nothing( Logger & logger )
	{
		flush( logger );
		logger.setMsgPriority( Nothing );
		return logger;
	}

	Logger & fatal( Logger & logger )
	{
		flush( logger );
		logger.setMsgPriority( All );
		return logger;
	}

	Logger & error( Logger & logger )
	{
		flush( logger );
		logger.setMsgPriority( Error );
		return logger;
	}

	Logger & info( Logger & logger )
	{
		flush( logger );
		logger.setMsgPriority( Info );
		return logger;
	}

	Logger & detail( Logger & logger )
	{
		flush( logger );
		logger.setMsgPriority( Detail );
		return logger;
	}

	Logger & debug( Logger & logger )
	{
		flush( logger );
		logger.setMsgPriority( Debug );
		return logger;
	}

	Logger & all( Logger & logger )
	{
		flush( logger );
		logger.setMsgPriority( All );
		return logger;
	}

	Logger & newLine( Logger & logger )
	{
		logger.writeNewLine();
		return logger;
	}

	Logger & watchdog( Logger & logger )
	{
		logger << "\tWatchDog:\t";
		return logger;
	}

	Logger & listener( Logger & logger )
	{
		logger << "\tListener:\t";
		return logger;
	}

	Logger & receiver( Logger & logger )
	{
		logger << "\tReceiver:\t";
		return logger;
	}

	Logger & sender( Logger & logger )
	{
		logger << "\tSender:\t";
		return logger;
	}

	Logger & analyzer( Logger & logger )
	{
		logger << "\tAnalyzer:\t";
		return logger;
	}

	Logger & archiver( Logger & logger )
	{
		logger << "\tArchiver:\t";
		return logger;
	}


	Logger & messageId( Logger & logger, unsigned long message_id )
	{
		logger << "MID: " << message_id << "\t";
		return logger;
	}

	Logger & connectionInId( Logger & logger, unsigned long connection_id )
	{
		logger << "CID.IN: " << connection_id << "\t";
		return logger;
	}

	Logger & connectionOutId( Logger & logger, unsigned long connection_id )
	{
		logger << "CID.OUT: " << connection_id << "\t";
		return logger;
	}

	LogId mid( unsigned long id )
	{
		return LogId( Mid, id );
	}

	LogId cidIn( unsigned long id )
	{
		return LogId( CidIn, id );
	}

	LogId cidOut( unsigned long id )
	{
		return LogId( CidOut, id );
	}

	Logger& operator<<( Logger& logger, const LogId & id)
	{
		id( logger );
		return logger ;
	}

}
